<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>画笔矩形框绘制工具</title>

    <style>
        * {
            margin: 0;
            padding: 0;
            touch-action:none;
        }


        .canvas-body {
            position: relative;
            width: 800px;
            height: 600px;
            border: 1px solid black;
            max-width: 96vw;
            max-height: 70vh;
            overflow: hidden;
            margin: 0 auto;
        }

        .canvas-warp {
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
        }

        .canvas-position {
            position: absolute;
            z-index: 0;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
        }

        .canvas {
            position: relative;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.2);
        }

        #toolbar {
            margin-bottom: 10px;
        }
    </style>

    <script src="https://unpkg.com/vconsole@3.15.1/dist/vconsole.min.js"></script>
    <script>
        const vConsole = new VConsole();
    </script>
</head>

<body>
    <div id="toolbar">
        <button onclick="setMode('draw')">Draw</button>
        <button onclick="setMode('rect')">Rectangle</button>
        <button onclick="undo()">Undo</button>
        <button onclick="redo()">Redo</button>
        <button onclick="toggleDisable()">Toggle Disable</button>
        <input type="color" id="colorPicker" value="#000000">
        <input type="color" id="fillColorPicker" value="#ffffff">
        <input type="range" id="brushSize" min="1" max="10" value="5">
        <input type="range" id="alpha" min="0" max="1" step="0.1" value="1">
    </div>

    <div class="canvas-body" id="canvasBody">
        <div class="canvas-warp" id="canvasWarp">
            <div class="canvas-position" id="canvasPosition">
                <canvas class="canvas" id="canvas"></canvas>
            </div>
        </div>
    </div>

    <script>

        const canvasBody = document.getElementById('canvasBody');
        const canvasWarp = document.getElementById('canvasWarp');
        const canvasPosition = document.getElementById('canvasPosition');
        const canvas = document.getElementById('canvas');

        const originalWidth = canvasBody.offsetWidth;
        const originalHeight = canvasBody.offsetHeight;

        const ctx = canvas.getContext('2d');

        
        let styleObj = {
            translateX: 0,
            translateY: 0,
            scale: 1,
        }

        canvasPosition.style.transform = `translate(${styleObj.translateX}px, ${styleObj.translateY}px)`;
        
        canvas.width = originalWidth;
        canvas.height = originalHeight;
        canvasWarp.style.width = originalWidth + 'px';
        canvasWarp.style.height = originalHeight + 'px';

        let canvasWidth = originalWidth
        let canvasHeight = originalHeight

        console.log(canvas.width, canvas.height)

        
        function updateWarpStyle() {
            // 确保我们操作的是正确的对象  
            if (styleObj && canvas) {
                // 计算 transform 字符串  
                let transform = `translate(${styleObj.translateX}px, ${styleObj.translateY}px)`;
                canvasPosition.style.transform = transform;

                let newCanvasWidth = styleObj.scale * originalWidth;
                let newCanvasHeight = styleObj.scale * originalHeight;

                let scaleChange = newCanvasWidth / canvasWidth;

                canvasWidth = newCanvasWidth;
                canvasHeight = newCanvasHeight;

                canvas.width = newCanvasWidth;
                canvas.height = newCanvasHeight;

                canvasWarp.style.width = canvas.width + 'px';
                canvasWarp.style.height = canvas.height + 'px';

                console.log('scaleChange', scaleChange, canvasWidth)

                // console.log('updateWarpStyle', canvas.width, canvas.height)
                history.forEach(step => {
                    step.buttonX = step.buttonX * scaleChange;
                    step.buttonY = step.buttonY * scaleChange;
                    step.size = step.size * scaleChange;
                    if (step.type === 'draw') {
                        for (let i = 0; i < step.points.length; i++) {
                            step.points[i].x = step.points[i].x * scaleChange
                            step.points[i].y = step.points[i].y * scaleChange
                        }
                    } else if (step.type === 'rect') {
                        step.rect.x = step.rect.x * scaleChange
                        step.rect.y = step.rect.y * scaleChange
                        step.rect.width = step.rect.width * scaleChange
                        step.rect.height = step.rect.height * scaleChange
                    }
                });

                redraw();
            }
        }

        let drawing = false;
        let mode = 'draw';
        let disable = false;
        let startX, startY;
        let history = [];
        let redoStack = [];
        let currentRect = null;

        ctx.lineCap = 'round';
        ctx.lineJoin = 'round';

        const buttonSize = 20;

        function setMode(newMode) {
            mode = newMode;
        }

        function toggleDisable() {
            disable = !disable;
        }

        function undo() {
            if (history.length > 0) {
                redoStack.push(history.pop());
                redraw();
            }
        }

        function redo() {
            if (redoStack.length > 0) {
                history.push(redoStack.pop());
                redraw();
            }
        }

        function deleteDrawing(index) {
            history.splice(index, 1);
            redraw();
        }

        function redraw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            history.forEach(step => {
                ctx.strokeStyle = step.color;
                ctx.fillStyle = step.fillColor;
                ctx.lineWidth = step.size;
                ctx.beginPath();
                let buttonX, buttonY;
                if (step.type === 'draw') {
                    ctx.moveTo(step.points[0].x, step.points[0].y);
                    for (let i = 1; i < step.points.length; i++) {
                        ctx.lineTo(step.points[i].x, step.points[i].y);
                    }

                    let lastPort = step.points[step.points.length - 1]
                    buttonX = step.buttonX || (lastPort.x - buttonSize / 2);
                    buttonY = step.buttonY || (lastPort.y - buttonSize / 2);
                } else if (step.type === 'rect') {
                    const rect = step.rect;
                    ctx.rect(rect.x, rect.y, rect.width, rect.height);
                    ctx.fill();

                    buttonX = step.buttonX || ((rect.x + rect.width) - buttonSize / 2);
                    buttonY = step.buttonY || ((rect.y + rect.height) - buttonSize / 2);
                }

                ctx.stroke();


                ctx.fillStyle = 'red';
                ctx.fillRect(buttonX, buttonY, buttonSize, buttonSize);
                ctx.fillStyle = 'white';
                ctx.fillText('X', buttonX + 5, buttonY + 15);

            });
        }

        function hexToRgba(hex, alpha) {
            let r = parseInt(hex.slice(1, 3), 16);
            let g = parseInt(hex.slice(3, 5), 16);
            let b = parseInt(hex.slice(5, 7), 16);
            return `rgba(${r}, ${g}, ${b}, ${alpha})`;
        }

        function mousedown(x, y) {
            if (disable) return;

            startX = x;
            startY = y;

            let hasButton = false
            for (let i = history.length - 1; i >= 0; i--) {
                const step = history[i];
                if (startX >= step.buttonX && startX <= step.buttonX + buttonSize && startY >= step.buttonY && startY <= step.buttonY + buttonSize) {
                    deleteDrawing(i);
                    hasButton = true
                    break;
                }
            }

            if (hasButton) return

            drawing = true;
            const alpha = document.getElementById('alpha').value;
            ctx.strokeStyle = hexToRgba(document.getElementById('colorPicker').value, alpha);
            ctx.fillStyle = hexToRgba(document.getElementById('fillColorPicker').value, alpha);
            ctx.lineWidth = document.getElementById('brushSize').value;
            if (mode === 'draw') {
                ctx.beginPath();
                ctx.moveTo(startX, startY);
                history.push({ type: 'draw', color: ctx.strokeStyle, size: ctx.lineWidth, points: [{ x: startX, y: startY }] });
            } else if (mode === 'rect') {
                currentRect = { x: startX, y: startY, width: 0, height: 0 };
            }
        }

        function mousemove(x, y) {
            if (disable || !drawing) return;

            if (mode === 'draw') {
                ctx.lineTo(x, y);
                ctx.stroke();
                history[history.length - 1].points.push({ x, y });
            } else if (mode === 'rect') {
                const rect = {
                    x: Math.min(startX, x),
                    y: Math.min(startY, y),
                    width: Math.abs(x - startX),
                    height: Math.abs(y - startY)
                };
                currentRect = rect;
                redraw();
                ctx.strokeStyle = hexToRgba(document.getElementById('colorPicker').value, document.getElementById('alpha').value);
                ctx.fillStyle = hexToRgba(document.getElementById('fillColorPicker').value, document.getElementById('alpha').value);
                ctx.lineWidth = document.getElementById('brushSize').value;
                ctx.beginPath();
                ctx.rect(rect.x, rect.y, rect.width, rect.height);
                ctx.fill();
                ctx.stroke();
            }
        }

        function mouseup(x, y) {
            if (disable || !drawing) return;
            drawing = false;

            let buttonX, buttonY;

            if (mode === 'draw') {
                let lastDraw = history[history.length - 1]
                let lastPort = lastDraw.points[lastDraw.points.length - 1]
                lastDraw.buttonX = buttonX = lastPort.x - buttonSize / 2;
                lastDraw.buttonY = buttonY = lastPort.y - buttonSize / 2;
            } else if (mode === 'rect') {
                const rect = currentRect;
                if (rect.width > 10 && rect.height > 10) {

                    buttonX = (x) - buttonSize / 2;
                    buttonY = (y) - buttonSize / 2;

                    history.push({ type: 'rect', color: ctx.strokeStyle, fillColor: ctx.fillStyle, size: ctx.lineWidth, rect, buttonX, buttonY });
                    currentRect = null;
                    redoStack = [];
                }
            }

            if (buttonX > 0 && buttonX > 0) {
                const buttonSize = 20;
                ctx.fillStyle = 'red';
                ctx.fillRect(buttonX, buttonY, buttonSize, buttonSize);
                ctx.fillStyle = 'white';
                ctx.fillText('X', buttonX + 5, buttonY + 15);
            }
        }

        canvas.addEventListener('mousedown', e => {
            mousedown(e.offsetX, e.offsetY)
        });
        canvas.addEventListener('mousemove', e => {
            mousemove(e.offsetX, e.offsetY)
        });
        canvas.addEventListener('mouseup', e => {
            mouseup(e.offsetX, e.offsetY)
        });

        let lastTouch
        let toucheLenght = 0;
        let initialDistance = 0;
        let initialScale = styleObj.scale;
        let lastCenterPoint = [0, 0];
        let initCenterPoint = [0, 0];
        let lastScaleDiff = [0, 0];
        let lastZoomOffset = [0, 0];
        let lastZoomOffsetRelative = [0, 0];
        // bodyContent

        canvas.addEventListener('touchstart', e => {
            e.preventDefault();

            const touches = e.touches

            if (touches.length === 1) {
                const touch = touches[0];
                const { left, top } = canvas.getBoundingClientRect();
                lastTouch = touch
                mousedown(touch.clientX - left, touch.clientY - top)
            } else {
                drawing = false;
                if (mode === 'draw' && toucheLenght == 1) {
                    history.pop()
                }
                redraw();
                // updateWarpStyle();

                const dx = touches[0].clientX - touches[1].clientX;
                const dy = touches[0].clientY - touches[1].clientY;

                lastCenterPoint = [(touches[0].clientX + touches[1].clientX) / 2, (touches[0].clientY + touches[1].clientY) / 2];
                initCenterPoint = [(touches[0].clientX + touches[1].clientX) / 2, (touches[0].clientY + touches[1].clientY) / 2];
                initialDistance = Math.sqrt(dx * dx + dy * dy);
                initialScale = styleObj.scale;

                lastZoomOffset = [((initialScale - 1) * canvas.width) / 2, ((initialScale - 1) * canvas.height) / 2];

                lastZoomOffsetRelative = [initCenterPoint[0] * (initialScale - 1), initCenterPoint[1] * (initialScale - 1)];

                // console.log(touches[0].clientX, touches[0].clientY)
                // console.log(touches[1].clientX, touches[1].clientY)
                // console.log('lastCenterPoint', lastCenterPoint)
            }
            toucheLenght = touches.length;
        });
        canvas.addEventListener('touchmove', e => {
            e.preventDefault();
            const touches = event.touches;
            if (touches.length === 1) {
                const touch = touches[0];
                const { left, top } = canvas.getBoundingClientRect();
                lastTouch = touch
                mousemove(touch.clientX - left, touch.clientY - top)
            } else {
                const dx = touches[0].clientX - touches[1].clientX;
                const dy = touches[0].clientY - touches[1].clientY;

                // 两点之间的距离
                const currentDistance = Math.sqrt(dx * dx + dy * dy);
                // 两个点的中心点坐标
                const currentCenterPoint = [(touches[0].clientX + touches[1].clientX) / 2, (touches[0].clientY + touches[1].clientY) / 2];

                // 计算缩放（相对touchstart的原scale）
                const scale = currentDistance / initialDistance;
                // 计算中心点位置移动
                const centerDiffX = currentCenterPoint[0] - lastCenterPoint[0];
                const centerDiffY = currentCenterPoint[1] - lastCenterPoint[1];


                // 缩放原中心点偏移量
                const zoomOffset = [((initialScale * scale - 1) * canvas.width) / 2, ((initialScale * scale - 1) * canvas.height) / 2];
                const zoomOffsetX = zoomOffset[0] - lastZoomOffset[0];
                const zoomOffsetY = zoomOffset[1] - lastZoomOffset[1];


                // 缩放两点中心偏移量relative

                const zoomOffsetRelative = [currentCenterPoint[0] * (scale - 1), currentCenterPoint[1] * (scale - 1)];
                // const zoomOffsetRelativeX = zoomOffsetRelative[0] - lastZoomOffsetRelative[0];
                // const zoomOffsetRelativeY = zoomOffsetRelative[1] - lastZoomOffsetRelative[1];

                const zoomOffsetRelativeX = 0;
                const zoomOffsetRelativeY = 0;



                styleObj.translateX = styleObj.translateX + centerDiffX;
                styleObj.translateY = styleObj.translateY + centerDiffY;
                styleObj.scale = initialScale * scale;
                updateWarpStyle();

                lastCenterPoint = currentCenterPoint
                // lastScaleDiff = [scaleDiffX, scaleDiffY]
                lastZoomOffset = zoomOffset

                lastZoomOffsetRelative = zoomOffsetRelative

            }
            toucheLenght = touches.length;
        });
        canvas.addEventListener('touchend', e => {
            e.preventDefault();

            toucheLenght = 0;

            if (styleObj.scale <= 1) {
                styleObj.translateX = 0;
                styleObj.translateY = 0;
                styleObj.scale = 1;

                updateWarpStyle();
            } else {
                // 平衡线的一半
                let balanceX = (canvasWidth - originalWidth) / 2;
                if(styleObj.translateX > 0 && styleObj.translateX > balanceX) {
                    styleObj.translateX = balanceX
                } else if (styleObj.translateX < 0 && styleObj.translateX < balanceX * -1) {
                    styleObj.translateX = balanceX * -1
                }

                // 平衡线的一半
                let balanceY = (canvasHeight - originalHeight) / 2;
                if(styleObj.translateY > 0 && styleObj.translateY > balanceY) {
                    styleObj.translateY = balanceY
                } else if (styleObj.translateY < 0 && styleObj.translateY < balanceY * -1) {
                    styleObj.translateY = balanceY * -1
                }

                updateWarpStyle();
            }

            if (lastTouch) {
                const { left, top } = canvas.getBoundingClientRect();
                mouseup(lastTouch.clientX - left, lastTouch.clientY - top)
            }


            setTimeout(() => {
                console.log(history)
            }, 10);
        });
    </script>
</body>

</html>